﻿
using NCP.Infrastructure.Common.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace NCP.Infrastructure.Common
{
    public static class ModuleExtensions
    {
        #region Methods
        #region 创建一个新对象实例
        /// <summary>
        /// 创建一个新对象实例
        /// </summary>
        /// <typeparam LanguageName="TEntity">参数类型</typeparam>
        /// <param name="entity">当前对象</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        /// <returns>创建的新对象</returns>
        public static T CopyItem<T>(this T entity, Action<T> action = null,
            bool isCache = true) where T : class, new()
        {
            var item = entity.CopyItem<T, T>(null, isCache);
            if (action != null)
            {
                action(item);
            }
            return item;
        }

        /// <summary>
        /// 创建一个新对象实例
        /// </summary>
        /// <typeparam LanguageName="TFrom">源类型</typeparam>
        /// <typeparam LanguageName="TTo">目标类型</typeparam>
        /// <param name="entity">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        public static void CopyItemTo<TFrom, TTo>(this TFrom entity, TTo target,
            Action<TFrom, TTo> action = null,
            bool isCache = true)
        {
            entity.CopyItemByNames(target, action, isCache);
        }
        #endregion

        #region 复制一个对象到另一个对象
        /// <summary>
        /// 复制一个对象到另一个对象
        /// </summary>
        /// <typeparam LanguageName="TEntity">参数类型</typeparam>
        /// <param name="entity">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        public static void CopyItemTo<T>(this T entity, T target, Action<T> action = null, bool isCache = true)
        {
            entity.CopyItemTo<T, T>(target, null, isCache);
            if (action != null)
            {
                action(target);
            }
        }

        /// <summary>
        /// 复制一个对象到另一个对象
        /// </summary>
        /// <typeparam LanguageName="TFrom">源类型</typeparam>
        /// <typeparam LanguageName="TTo">目标类型</typeparam>
        /// <param name="entity">源对象</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        /// <returns>复制后的对象</returns>
        public static TTo CopyItem<TFrom, TTo>(this TFrom entity,
            Action<TFrom, TTo> action = null, bool isCache = true)
            where TTo : class, new()
        {
            var newEntity = new TTo();
            entity.CopyItemTo<TFrom, TTo>(newEntity, action, isCache);
            return newEntity;
        }

        /// <summary>
        /// 根据属性名称拷贝
        /// </summary>
        /// <typeparam LanguageName="TFrom">源类型</typeparam>
        /// <typeparam LanguageName="TTo">目标类型</typeparam>
        /// <param name="fromEntity">源对象</param>
        /// <param name="toEntity">目标对象</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        /// <param name="propertyNames">需要拷贝的属性名称</param>
        public static void CopyItemByNames<TFrom, TTo>(this TFrom fromEntity,
            TTo toEntity, Action<TFrom, TTo> action = null, bool isCache = true,
            params string[] propertyNames)
        {
            var fromType = typeof(TFrom);
            var toType = typeof(TTo);
            var properties = from fromProperty in fromType.GetProperties().Where(item => item.CanRead)
                             join toProperty in toType.GetProperties().Where(item => item.CanWrite)
                               on new { Name = fromProperty.Name.ToLower(), fromProperty.PropertyType }
                                equals new { Name = toProperty.Name.ToLower(), toProperty.PropertyType }
                             select
                             new
                             {
                                 fromProperty,
                                 toProperty,
                                 Name = fromProperty.Name
                             };

            if (propertyNames.Length > 0)
            {
                properties = properties.Where(item => propertyNames.Contains(item.Name));
            }

            foreach (var property in properties)
            {
                if (property.Name.ToLower().Equals("id"))
                    continue;
                fromEntity.CopyValue(toEntity, property.fromProperty, property.toProperty, isCache);
            }

            if (action != null)
            {
                action(fromEntity, toEntity);
            }
        }
        #endregion

        #region 设定值
        /// <summary>
        /// 设定值
        /// </summary>
        /// <typeparam LanguageName="TFrom">源类型</typeparam>
        /// <typeparam LanguageName="TTo">目标类型</typeparam>
        /// <param name="fromEntity">源对象</param>
        /// <param name="toEntity">目标对象</param>
        /// <param name="property">属性</param>
        /// <param name="isCache">用于列表深度复制的参数</param>
        private static void CopyValue<TFrom, TTo>(this TFrom fromEntity, TTo toEntity,
            PropertyInfo property, PropertyInfo toProperty, bool isCache = true)
        {
            if (property.PropertyType.Equals(typeof(string)))
            {
                property.GetAndSetProperty<TFrom, TTo, string>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(int)))
            {
                property.GetAndSetProperty<TFrom, TTo, int>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(int?)))
            {
                property.GetAndSetProperty<TFrom, TTo, int?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(long)))
            {
                property.GetAndSetProperty<TFrom, TTo, long>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(long?)))
            {
                property.GetAndSetProperty<TFrom, TTo, long?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(decimal)))
            {
                property.GetAndSetProperty<TFrom, TTo, decimal>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(decimal?)))
            {
                property.GetAndSetProperty<TFrom, TTo, decimal?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(double)))
            {
                property.GetAndSetProperty<TFrom, TTo, double>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(double?)))
            {
                property.GetAndSetProperty<TFrom, TTo, double?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(DateTime)))
            {
                property.GetAndSetProperty<TFrom, TTo, DateTime>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(DateTime?)))
            {
                property.GetAndSetProperty<TFrom, TTo, DateTime?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(bool)))
            {
                property.GetAndSetProperty<TFrom, TTo, bool>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(bool?)))
            {
                property.GetAndSetProperty<TFrom, TTo, bool?>(toProperty, fromEntity, toEntity, isCache);
            }
            else if (property.PropertyType.Equals(typeof(byte[])))
            {
                property.GetAndSetProperty<TFrom, TTo, byte[]>(toProperty, fromEntity, toEntity, isCache);
            }
            else
            {
                property.GetAndSetProperty<TFrom, TTo, object>(toProperty, fromEntity, toEntity, isCache);
            }
        }
        #endregion
        #endregion

        public static void SetLovName<T>(this T currentItem)
            where T : class
        {
            var searchList = from idProperty in typeof(T).GetProperties().Where(s => s.CanRead && s.PropertyType == typeof(int?))
                             join nameProperty in typeof(T).GetProperties().Where(s => s.CanWrite && s.PropertyType == typeof(string))
                             on new { Name = idProperty.Name + "Name" } equals new { Name = nameProperty.Name }
                             select new
                             {
                                 ReadProperty = idProperty,
                                 WriteProperty = nameProperty
                             };
            foreach (var item in searchList)
            {
                var lovValue = ConvertLovValueToGuid((int?)item.ReadProperty.GetValue(currentItem, null));
                var lovName = LovHandler.GetNameById(lovValue);
                item.WriteProperty.SetValue(currentItem, lovName, null);
            }
        }

        private static Guid? ConvertLovValueToGuid(int? lovIntValue)
        {
            if (!lovIntValue.HasValue)
            {
                return null;
            }
            var lovTypeValue = lovIntValue.ToString();
            //格式化：例如 201000 格式化成 00000000-0000-0000-0000-000000201000 的形式
            var regex = new Regex(@"\d{" + lovTypeValue.Length + "}$");
            var id = Guid.Parse(regex.Replace(Guid.Empty.ToString(), lovTypeValue));
            return id;
        }

        public static List<TTo> CopyListTo<TFrom, TTo>(this IEnumerable<TFrom> list)
            where TFrom : class,new()
            where TTo : class,new()
        {
            return (list ?? new List<TFrom>()).ToList().ConvertAll<TTo>(
                item =>
                {
                    var result = item.CopyItem<TFrom, TTo>();
                    result.SetLovName();
                    return result;
                });
        }
    }
}
